package com.kmxd.ams.app.arrange;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.*;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.ImmutableMap;
import com.kmxd.ams.app.arrange.command.*;
import com.kmxd.ams.app.event.AmsEventPublisher;
import com.kmxd.ams.client.archive.*;
import com.kmxd.ams.client.archive.dto.*;
import com.kmxd.ams.client.archive.vo.*;
import com.kmxd.ams.client.arrange.*;
import com.kmxd.ams.client.arrange.dto.*;
import com.kmxd.ams.client.arrange.vo.*;
import com.kmxd.ams.client.event.involume.ChangeInVolumeArchCodeEvent;
import com.kmxd.ams.client.event.log.DocumentLogEvent;
import com.kmxd.ams.client.event.log.VolumeLogEvent;
import com.kmxd.ams.client.oss.IEfileRecordSvc;
import com.kmxd.ams.client.oss.vo.EfileRecordListVO;
import com.kmxd.ams.client.system.ISysDictSvc;
import com.kmxd.ams.client.system.vo.SysDictListVO;
import com.kmxd.ams.core.common.constant.ArchConst;
import com.kmxd.ams.core.common.constant.Constants;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.common.enums.*;
import com.kmxd.ams.core.exception.BizTipException;
import com.kmxd.ams.core.exception.DataCheckException;
import com.kmxd.ams.core.util.*;
import com.kmxd.ams.core.util.IdUtil;
import com.kmxd.ams.infra.archive.entity.ArchiveRuleMain;
import com.kmxd.ams.infra.archive.entity.ArchiveType;
import com.kmxd.ams.infra.arrange.base.BaseBO;
import com.kmxd.ams.infra.arrange.base.SystemBO;
import com.kmxd.ams.infra.arrange.entity.*;
import com.kmxd.ams.infra.tmpl.dto.ArchTmplBO;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * 档案整理通用接口 服务实现类
 *
 * @author ZuoLG
 * @since 2022-07-20
 */
@Slf4j
@Service
@AllArgsConstructor
public class ArranageCommonSvcImpl implements IArrangeCommonSvc {

  private final IArchiveRuleFieldSvc archiveRuleFieldSvc;
  private final AssociatedFilesCmd associatedFilesCmd;
  private final MergeDocumentCmd mergeDocumentCmd;
  private final ChangeSortCmd changeSortCmd;
  private final ExtendDataCmd extendDataCmd;
  private final ArchCodeGenCmd archCodeGenCmd;
  private final CheckCommonCmd checkCommonCmd;
  private final BreakUpCmd breakUpCmd;
  private final InVolumeCmd inVolumeCmd;
  private final InBoxCmd inBoxCmd;
  private final RefreshCatalogCmd refreshCatalogCmd;
  private final AmsEventPublisher amsEventPublisher;
  private final IArchiveOrderFieldSvc archiveOrderFieldSvc;
  private final DataRepeatCmd dataRepeatCmd;
  // 用于归档校验
  private final ArchivingCmd archivingCmd;
  private IArchiveRuleMainSvc archiveRuleMainSvc;
  private IArchiveFieldSvc archiveFieldSvc;
  private IArchiveTypeSvc archiveTypeSvc;
  private IEfileRecordSvc efileRecordSvc;
  private RetentionCmd retentionCmd;
  private ISysDictSvc sysDictSvc;
  private IDocumentSvc documentSvc;
  private IProjectSvc projectSvc;
  private IVolumeSvc volumeSvc;
  private IBoxSvc boxSvc;

  @Override
  public List<ClumnsListVO> getClumns(BaseQueryDTO req) {
    if (ObjectUtil.isAllEmpty(req.getTableId(), req.getArchTypeId())) {
      throw BizTipException.instance(ErrorCode.NOT_FOUND, "必传参数为空！");
    }
    if (ObjectUtil.isEmpty(req.getTableId())) {
      // 如果没有设置表名，默认取文件级表名
      ArchiveTypeVO archiveTypeVO = archiveTypeSvc.view(req.getArchTypeId());
      req.setTableId(archiveTypeVO.getDocTableId());
    }

    ArchiveFieldQueryDTO queryDTO = BeanUtil.copyProperties(req, ArchiveFieldQueryDTO.class);
    queryDTO.setAttrList(BooleanUtil.toBoolean(YesNoEnum.YES.getCode()));
    List<ArchiveFieldListVO> list = archiveFieldSvc.selectListClumns(queryDTO);

    return list.stream()
        .map(
            item ->
                ClumnsListVO.builder()
                    .title(item.getDbFieldName())
                    .width(
                        item.getHeaderWidth() == null || item.getHeaderWidth() == 0
                            ? 60
                            : item.getHeaderWidth())
                    .dataIndex(
                        !StringUtils.isCamel(item.getDbFieldKey())
                            ? StringUtils.underlineToCamel(item.getDbFieldKey())
                            : item.getDbFieldKey())
                    .sorter(true)
                    .align(item.getDbFieldKey().equals("title") ? "left" : "center")
                    .resizable(true)
                    .defaultHidden(!BooleanUtil.isTrue(item.getAttrList()))
                    .build())
        .collect(Collectors.toList());
  }

  @Override
  public ClumnsBoxListVO getClumnsByBox(BaseQueryDTO req) {
    ArchiveType archiveType = archiveTypeSvc.getById(req.getArchTypeId());
    req.setTableId(archiveType.getDocTableId());
    List<ClumnsListVO> docList = getClumns(req);
    req.setTableId(archiveType.getBoxTableId());
    return ClumnsBoxListVO.builder().docClumns(docList).boxClumns(getClumns(req)).build();
  }

  @Override
  public ClumnsVolumeListVO getClumnsByVolume(BaseQueryDTO req) {
    ArchiveType archiveType = archiveTypeSvc.getById(req.getArchTypeId());
    req.setTableId(archiveType.getDocTableId());
    List<ClumnsListVO> docList = getClumns(req);
    req.setTableId(archiveType.getVolTableId());
    return ClumnsVolumeListVO.builder().docClumns(docList).volClumns(getClumns(req)).build();
  }

  @Override
  public List<ClumnsListVO> getClumnsV2(BaseQueryDTO req) {
    if (ObjectUtil.isAllEmpty(req.getTableId(), req.getArchTypeId())) {
      throw BizTipException.instance(ErrorCode.NOT_FOUND, "必传参数为空！");
    }
    if (ObjectUtil.isEmpty(req.getTableId())) {
      // 如果没有设置表名，默认取文件级表名
      ArchiveTypeVO archiveTypeVO = archiveTypeSvc.view(req.getArchTypeId());
      req.setTableId(archiveTypeVO.getDocTableId());
    }

    ArchiveFieldQueryDTO queryDTO = BeanUtil.copyProperties(req, ArchiveFieldQueryDTO.class);
    queryDTO.setAttrList(BooleanUtil.toBoolean(YesNoEnum.YES.getCode()));
    List<ArchiveFieldListVO> list = archiveFieldSvc.selectListClumns(queryDTO);

    JSONObject filesNum = JSONUtil.createObj();
    filesNum.putOpt("default", "filesNumSlot");

    // 判断是否是文件级
    boolean isBox = DynamicTableNameUtil.checkTableBox(req.getTableId());
    boolean isPorj = DynamicTableNameUtil.checkTableProj(req.getTableId());
    boolean tableFlag = isBox || isPorj;

    List<ClumnsListVO> lists =
        list.stream()
            .map(
                item -> {
                  String field =
                      !StringUtils.isCamel(item.getDbFieldKey())
                          ? StringUtils.underlineToCamel(item.getDbFieldKey())
                          : item.getDbFieldKey();
                  return ClumnsListVO.builder()
                      .title(item.getDbFieldName())
                      .width(
                          tableFlag
                                  && (ObjectUtil.equal(field, "title")
                                      || ObjectUtil.equal(field, "classfyName"))
                              ? null
                              : item.getHeaderWidth() == null || item.getHeaderWidth() == 0
                                  ? 60
                                  : item.getHeaderWidth())
                      .dataIndex(field)
                      .sortable(Boolean.TRUE)
                      .sorter(true)
                      .align(item.getDbFieldKey().equals("title") ? "left" : "center")
                      .resizable(true)
                      .slots(ObjectUtil.notEqual(field, "filesNum") ? null : filesNum)
                      .defaultHidden(!BooleanUtil.isTrue(item.getAttrList()))
                      .build();
                })
            .collect(Collectors.toList());
    // 增加选择框插槽
    ClumnsListVO checkBox =
        ClumnsListVO.builder().width(40).type("checkbox").align("center").fixed("left").build();

    // 增加序号列
    ClumnsListVO seq =
        ClumnsListVO.builder().width(60).type("seq").align("center").fixed("left").build();

    // 增加操作插槽
    JSONObject actionSlot = JSONUtil.createObj();
    actionSlot.putOpt("default", "action");
    ClumnsListVO action =
        ClumnsListVO.builder()
            .width(isPorj ? 160 : 65)
            .title("操作")
            .align("center")
            .resizable(true)
            .fixed("right")
            .slots(actionSlot)
            .build();
    lists.add(0, seq);
    lists.add(0, checkBox);
    lists.add(action);
    return lists;
  }

  @Override
  public ClumnsBoxListVO getClumnsByBoxV2(BaseQueryDTO req) {
    ArchiveType archiveType = archiveTypeSvc.getById(req.getArchTypeId());
    req.setTableId(archiveType.getDocTableId());
    List<ClumnsListVO> docList = getClumnsV2(req);
    req.setTableId(archiveType.getBoxTableId());
    return ClumnsBoxListVO.builder().docClumns(docList).boxClumns(getClumnsV2(req)).build();
  }

  @Override
  public ClumnsVolumeListVO getClumnsByVolumeV2(BaseQueryDTO req) {
    ArchiveType archiveType = archiveTypeSvc.getById(req.getArchTypeId());
    req.setTableId(archiveType.getDocTableId());
    List<ClumnsListVO> docList = getClumnsV2(req);
    req.setTableId(archiveType.getVolTableId());
    return ClumnsVolumeListVO.builder().docClumns(docList).volClumns(getClumnsV2(req)).build();
  }

  @Override
  public List<ClumnsQueryVO> getClumnsByQuery(BaseQueryDTO req) {
    ArchiveFieldQueryDTO queryDTO = BeanUtil.copyProperties(req, ArchiveFieldQueryDTO.class);
    queryDTO.setAttrList(BooleanUtil.toBoolean(YesNoEnum.YES.getCode()));
    List<ArchiveFieldListVO> list = archiveFieldSvc.selectList(queryDTO);
    return list.stream()
        .map(item -> BeanUtil.copyProperties(item, ClumnsQueryVO.class))
        .collect(Collectors.toList());
  }

  @Override
  public FormVO getFormSchema(BaseQueryDTO req) {
    ArchiveTypeVO archiveType = archiveTypeSvc.view(req.getArchTypeId());
    ArchiveFieldQueryDTO queryDTO = BeanUtil.copyProperties(req, ArchiveFieldQueryDTO.class);
    List<ArchiveFieldListVO> listDb = archiveFieldSvc.selectListByTableId(queryDTO.getTableId());
    List<ArchiveFieldListVO> listVO =
        listDb.stream()
            .filter(
                x -> {
                  if (ObjectUtil.isNotEmpty(req.getArchTypeId())) {
                    return ObjectUtil.equals(x.getArchTypeId(), req.getArchTypeId());
                  }
                  if (ObjectUtil.isNotEmpty(req.getTableId())) {
                    return ObjectUtil.equals(x.getTableId(), req.getTableId());
                  }
                  if (ObjectUtil.isNotEmpty(req.getDbFieldKeyList())) {
                    return req.getDbFieldKeyList().contains(x.getDbFieldKey());
                  }
                  return true;
                })
            .sorted(
                Comparator.comparing(ArchiveFieldListVO::getFieldGroup)
                    .thenComparing(ArchiveFieldListVO::getSort))
            .collect(Collectors.toList());
    List<ArchiveFieldListVO> list =
        listVO.stream()
            .sorted(
                Comparator.comparing(ArchiveFieldListVO::getFieldGroup)
                    .thenComparing(ArchiveFieldListVO::getSort))
            .collect(Collectors.toList());

    Map<Long, List<ArchiveFieldListVO>> collectForm =
        list.stream()
            .filter(x -> BooleanUtil.isTrue(x.getAttrUse()))
            .peek(
                item -> {
                  if (ObjectUtil.isNull(item.getFieldGroup())) {
                    item.setFieldGroup(FieldGroupEnum.OTHER.getCode());
                  }
                })
            .sorted(
                Comparator.comparing(ArchiveFieldListVO::getFieldGroup)
                    .thenComparing(ArchiveFieldListVO::getSort))
            .collect(Collectors.groupingBy(ArchiveFieldListVO::getFieldGroup));
    List<FormSchemaVO> schemaList = Lists.newArrayList();
    // 追加复制字段
    List<FormGroupVO> copyField =
        list.stream()
            .filter(x -> BooleanUtil.isTrue(x.getAttrCopy()))
            .map(
                item ->
                    FormGroupVO.builder()
                        .field(
                            !StringUtils.isCamel(item.getDbFieldKey())
                                ? StringUtils.underlineToCamel(item.getDbFieldKey())
                                : item.getDbFieldKey())
                        .build())
            .collect(Collectors.toList());

    // 循环组装数据
    collectForm.forEach(
        (key, fieldItems) -> {
          FieldGroupEnum anEnum = FieldGroupEnum.getEnum(key);
          schemaList.add(
              FormSchemaVO.builder()
                  .field("Divider" + key)
                  .component("Divider")
                  .label(anEnum.getDesc())
                  .componentProps(JSONUtil.createObj().putOpt("style", dividerStyle()))
                  .colProps(JSONUtil.createObj().putOpt("span", 24))
                  .show(true)
                  .build());
          schemaList.addAll(
              conversionList(fieldItems, archiveType, req.getYear(), req.getStatus()));
        });
    // 获取分组规则
    RuleFieldQueryDTO fieldQueryDTO = BeanUtil.copyProperties(req, RuleFieldQueryDTO.class);
    boolean defaultYear =
        list.stream()
            .anyMatch(
                item ->
                    ArchConst.FIELD_YEAR.equals(item.getDbFieldKey())
                        && ArchConst.DEF_YEAR.equals(item.getWidgetDefault()));
    if (Objects.nonNull(req.getYear())) {
      fieldQueryDTO.setYear(req.getYear());
    }
    if (Objects.isNull(req.getYear()) && defaultYear) {
      fieldQueryDTO.setYear(DateUtil.year(new Date()));
    }
    setRuleByProjId(req.getArchTypeId(), req.getProjId(), fieldQueryDTO);
    List<ArchiveRuleFieldListVO> lists = archiveRuleMainSvc.getRuleFieldByVersion(fieldQueryDTO);

    List<FormGroupVO> groupVo =
        lists.stream()
            .filter(
                item ->
                    !StrUtil.equals(item.getFieldCode(), item.getFieldName())
                        && !StrUtil.equals(item.getFieldCode(), ArchConst.DOC_SEQUENCE_DB)
                        && !StrUtil.equals(item.getFieldCode(), ArchConst.VOL_SEQUENCE_DB))
            .map(
                item ->
                    FormGroupVO.builder()
                        .field(
                            !StringUtils.isCamel(item.getFieldCode())
                                ? StringUtils.underlineToCamel(item.getFieldCode())
                                : item.getFieldCode())
                        .build())
            .collect(Collectors.toList());
    // 年度改变，清除值字段,及分类字段及分类名称
    List<SysDictListVO> dicts = sysDictSvc.selectListAll();
    List<FormGroupVO> cleanGroup =
        list.stream()
            .filter(
                (item) -> {
                  if (ObjectUtil.equals(item.getDbFieldKey(), ArchConst.CLASSFY)
                      || ObjectUtil.equals(item.getDbFieldKey(), ArchConst.CLASSFY_NAME)) {
                    return true;
                  }
                  if (StrUtil.isNotBlank(item.getDictField())) {
                    SysDictListVO sysDict =
                        dicts.stream()
                            .filter(dict -> dict.getDictCode().equals(item.getDictField()))
                            .findFirst()
                            .orElse(new SysDictListVO());
                    return BooleanUtil.isTrue(sysDict.getManySeries());
                  }
                  return false;
                })
            .map(
                item ->
                    FormGroupVO.builder()
                        .field(
                            !StringUtils.isCamel(item.getDbFieldKey())
                                ? StringUtils.underlineToCamel(item.getDbFieldKey())
                                : item.getDbFieldKey())
                        .build())
            .collect(Collectors.toList());

    return FormVO.builder()
        .formSchema(schemaList)
        .formGroup(groupVo)
        .copyField(copyField)
        .cleanField(cleanGroup)
        .build();
  }

  /**
   * 项目级查询整理规则
   *
   * @param archTypeId
   * @param projId
   * @param fieldQueryDTO
   */
  private void setRuleByProjId(Long archTypeId, Long projId, RuleFieldQueryDTO fieldQueryDTO) {
    if (Objects.nonNull(projId)) {
      ArchiveType archiveType = archiveTypeSvc.getById(archTypeId);
      if (Objects.isNull(archiveType)) {
        return;
      }
      Project qry = new Project();
      qry.setArchTypeId(archiveType.getId());
      qry.setId(projId);
      qry.setTableId(archiveType.getProjTableId());
      Project project = projectSvc.view(qry);
      fieldQueryDTO.setRuleId(project.getArchRuleId());
    }
  }

  @Override
  public List<FormFieldVO> getFormField(BaseQueryDTO req) {
    ArchiveFieldQueryDTO queryDTO = BeanUtil.copyProperties(req, ArchiveFieldQueryDTO.class);
    queryDTO.setAttrUse(BooleanUtil.toBoolean(YesNoEnum.YES.getCode()));
    List<ArchiveFieldListVO> list = archiveFieldSvc.selectList(queryDTO);

    // 循环组装数据
    List<FormFieldVO> lists =
        list.stream()
            .map(
                (item) ->
                    FormFieldVO.builder()
                        .value(
                            !StringUtils.isCamel(item.getDbFieldKey())
                                ? StringUtils.underlineToCamel(item.getDbFieldKey())
                                : item.getDbFieldKey())
                        .label(item.getDbFieldName())
                        .build())
            .collect(Collectors.toList());

    return lists;
  }

  /**
   * list对象转换
   *
   * @param list
   * @param archiveType
   * @param year
   * @param status
   * @return
   */
  private List<FormSchemaVO> conversionList(
      List<ArchiveFieldListVO> list, ArchiveTypeVO archiveType, Integer year, Integer status) {
    boolean defaultYear =
        list.stream()
            .anyMatch(
                item ->
                    ArchConst.FIELD_YEAR.equals(item.getDbFieldKey())
                        && ArchConst.DEF_YEAR.equals(item.getWidgetDefault()));
    return list.stream()
        .map(
            item -> {
              JSONObject componentProps =
                  JSONUtil.createObj().putOpt("placeholder", "请输入" + item.getDbFieldName());
              String widget = StrUtil.isNotBlank(item.getWidget()) ? item.getWidget() : "Input";
              String newWidget = widget;
              // 判断是否包含字典,并且类型只能是下拉选择或者多选
              if (StrUtil.isNotBlank(item.getDictField())
                  && (StrUtil.equals(widget, ArchConst.API_SELECT)
                      || StrUtil.equals(widget, ArchConst.MULTIPLE))) {
                newWidget = ArchConst.API_SELECT;
                JSONObject params = JSONUtil.createObj().putOpt("dictCode", item.getDictField());
                // 处理密级
                if (item.getDictField().equalsIgnoreCase(ArchConst.DICT_SECURITY)) {
                  params.putOpt("setCode", archiveType.getSecretCodeId());
                  // 编辑追加，传递year参数到后端设置组件
                  if (Objects.nonNull(year)) {
                    params.putOpt("year", year);
                  } else if (defaultYear) {

                    params.putOpt("year", DateUtil.year(new Date()));
                  }
                }
                // 处理保管期限
                if (item.getDictField().equalsIgnoreCase(ArchConst.DICT_RETENTIONPERIOD)) {
                  params.putOpt("setCode", archiveType.getRpCodeId());
                  // 编辑追加，传递year参数到后端设置组件
                  if (Objects.nonNull(year)) {
                    params.putOpt("year", year);
                  } else if (defaultYear) {

                    params.putOpt("year", DateUtil.year(new Date()));
                  }
                }
                componentProps.putOpt("params", params);
                // 是否立即加载
                componentProps.putOpt("immediate", true);
                // 表示特殊处理api
                componentProps.putOpt("apiLoad", ArchConst.LIST_DICT_MODEL);
                // 设置字典显示值
                componentProps.set(
                    "labelField",
                    StrUtil.isNotBlank(item.getDictShow()) ? item.getDictShow() : "label");
                // 设置字典保存值
                componentProps.set(
                    "valueField",
                    StrUtil.isNotBlank(item.getDictStorage()) ? item.getDictStorage() : "value");
                // 判断是否多选
                componentProps.putOpt(
                    "mode",
                    StrUtil.equals(item.getWidget(), ArchConst.MULTIPLE) ? "multiple" : "combobox");
              }
              // 判断是否部门树
              if (StrUtil.equals(widget, ArchConst.SELECT_DEPT_TREE)) {
                newWidget = "ApiTreeSelect";
                componentProps.putOpt("params", null);
                // 是否立即加载
                componentProps.putOpt("immediate", true);
                // apiLoad表示是选择部门
                componentProps.putOpt("apiLoad", ArchConst.SELECT_DEPT_TREE);
                componentProps.putOpt("api", ArchConst.SELECT_DEPT_TREE);
                componentProps.putOpt("resultField", "'data'");
                componentProps.putOpt(
                    "fieldNames",
                    JSONUtil.createObj()
                        .putOpt("label", "name")
                        .putOpt("key", "id")
                        .putOpt("value", "id"));
              }
              // 时间特殊处理
              if (StrUtil.equals(widget, ArchConst.DATE_PICKER_YEAR)) {
                newWidget = ArchConst.DATE_PICKER;
                componentProps.putOpt("format", DatePattern.NORM_YEAR_PATTERN.toUpperCase());
                componentProps.putOpt("valueFormat", DatePattern.NORM_YEAR_PATTERN.toUpperCase());
                componentProps.putOpt("picker", "year");
              } else if (StrUtil.equals(widget, ArchConst.DATE_PICKER_TIME)) {
                newWidget = ArchConst.DATE_PICKER;
                componentProps.putOpt("format", "YYYY-MM-DD HH:mm:ss");
                componentProps.putOpt("valueFormat", "YYYY-MM-DD HH:mm:ss");
                componentProps.putOpt("showTime", true);
              } else if (StrUtil.equals(widget, ArchConst.DATE_PICKER)) {
                newWidget = ArchConst.DATE_PICKER;
                componentProps.putOpt("format", "YYYYMMDD");
                componentProps.putOpt("valueFormat", "YYYYMMDD");
              }
              // 分类特殊处理 ，分类号(classfy)，分类名称(classfy_name)
              if (StrUtil.equalsIgnoreCase(item.getDbFieldKey(), ArchConst.CLASSFY)
                  || StrUtil.equalsIgnoreCase(item.getDbFieldKey(), ArchConst.CLASSFY_NAME)) {
                newWidget = ArchConst.API_TREE_SELECT;
                JSONObject params =
                    JSONUtil.createObj()
                        .putOpt("archTypeId", item.getArchTypeId().toString())
                        .putOpt("tableId", item.getTableId());
                // 编辑追加，传递year参数到后端设置组件
                if (Objects.nonNull(year)) {
                  params.putOpt("year", year);
                } else if (defaultYear) {

                  params.putOpt("year", DateUtil.year(new Date()));
                }

                componentProps.putOpt("params", params);
                // 是否立即加载
                componentProps.putOpt("immediate", true);
                // 表示特殊处理api
                componentProps.putOpt("apiLoad", ArchConst.LIST_CLASSIFY_TREE);
                componentProps.putOpt("api", ArchConst.LIST_CLASSIFY_TREE);
                componentProps.putOpt("resultField", "'data'");
                componentProps.putOpt(
                    "fieldNames",
                    JSONUtil.createObj()
                        // 分类号 分类名称 label统一显示拼接的name
                        .putOpt("label", "name")
                        .putOpt("key", "id")
                        .putOpt("value", "name"));
              }
              // 特殊处理状态
              if (StrUtil.equalsIgnoreCase(item.getDbFieldKey(), ArchConst.ARCH_STATUS)) {
                String statusIdctCode =
                    ObjectUtil.isNotEmpty(archiveType.getProjTableId())
                            && archiveType.getProjTableId().contains(ArchTmplBO.PROJECT)
                        ? "arch_status_proj"
                        : "arch_status";
                JSONObject params = JSONUtil.createObj().putOpt("dictCode", statusIdctCode);
                componentProps.putOpt("params", params);
                // 表示特殊处理api
                componentProps.putOpt("apiLoad", ArchConst.LIST_DICT_MODEL);
                // 设置显示值
                componentProps.set("labelField", "label");
                // 设置保存值
                componentProps.set("valueField", "value");
              }

              // 选择规则版本
              if (StrUtil.equalsIgnoreCase(item.getDbFieldKey(), ArchConst.ARCH_RULE_ID)) {
                newWidget = ArchConst.API_SELECT;
                JSONObject params =
                    JSONUtil.createObj().putOpt("archTypeId", item.getArchTypeId().toString());
                componentProps.putOpt("params", params);
                // 表示特殊处理api
                componentProps.putOpt("apiLoad", ArchConst.ARCH_RULE_ID);
                // 设置显示值
                componentProps.set("labelField", "name");
                // 设置保存值
                componentProps.set("valueField", "id");
              }

              // 数字框
              if (StrUtil.equalsIgnoreCase(widget, ArchConst.INPUT_NUMBER)) {
                componentProps.set("min", 0);
              }

              // 处理框长度问题
              componentProps.set("style", JSONUtil.createObj().putOpt("width", "100%"));
              // 设置是否启用禁用, 时间类有禁用问题
              componentProps.putOpt("disabled", !BooleanUtil.isTrue(item.getAttrEdit()));

              return FormSchemaVO.builder()
                  .id(item.getId())
                  .field(
                      !StringUtils.isCamel(item.getDbFieldKey())
                          ? StringUtils.underlineToCamel(item.getDbFieldKey())
                          : item.getDbFieldKey())
                  .label(item.getDbFieldName())
                  .component(newWidget)
                  .required(
                      (BooleanUtil.isTrue(item.getAttrEmptyWarn())
                              && ArchStatusEnum.S0.getCode().equals(status))
                          || (BooleanUtil.isTrue(item.getAttrEmptyWarnArrange())
                              && ArchStatusEnum.S1.getCode().equals(status)))
                  .colProps(
                      JSONUtil.createObj()
                          .putOpt(
                              "span",
                              ObjectUtil.isNotEmpty(item.getColSpan())
                                  ? item.getColSpan()
                                  : BooleanUtil.isTrue(item.getAttrUse()) ? 6 : 0))
                  .defaultValue(defValue(item))
                  .componentProps(componentProps)
                  .itemProps(JSONUtil.createObj().putOpt("validateTrigger", "blur"))
                  .show(BooleanUtil.isTrue(item.getAttrUse()))
                  .build();
            })
        .collect(Collectors.toList());
  }

  /**
   * 设置默认值
   *
   * @return
   */
  private String defValue(ArchiveFieldListVO item) {
    String defaultValue = null;
    String key =
        StrUtil.isNotBlank(item.getWidgetDefault())
            ? item.getWidgetDefault()
            : item.getDbFieldKey();
    switch (key) {
      case ArchConst.DEF_ID:
        defaultValue = String.valueOf(IdWorker.getId());
        break;
      case ArchConst.DEF_FONDS_ID:
        defaultValue = String.valueOf(SecurityUtil.getFondsId());
        break;
      case ArchConst.DEF_FONDS_CODE:
        defaultValue = String.valueOf(SecurityUtil.getFondsCode());
        break;
      case ArchConst.DEF_STATUS:
        defaultValue = ArchStatusEnum.S0.getCode().toString();
        break;
      case ArchConst.DEF_SOURCE_TYPE:
        defaultValue = ArchStatusEnum.S1.getCode().toString();
        break;
      case ArchConst.DEF_DATE:
        defaultValue = DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN);
        break;
      case ArchConst.DEF_YEAR:
        defaultValue = DateUtil.format(DateUtil.date(), DatePattern.NORM_YEAR_PATTERN);
        break;
      case ArchConst.DEF_USER_ID:
        defaultValue = String.valueOf(SecurityUtil.getUserId());
        break;
      case ArchConst.DEF_USER_NAME:
        defaultValue = String.valueOf(SecurityUtil.getUsername());
        break;
      case ArchConst.DEF_DEL_FLAG:
        defaultValue = String.valueOf(DelFlagEnum.YES.getCode());
        break;
      case ArchConst.DEF_DEPT_ID:
        defaultValue = String.valueOf(SecurityUtil.getSysUser().getDeptId());
        break;
      case ArchConst.DEF_DEPT_NAME:
        defaultValue = SecurityUtil.getLoginUser().getDeptName();
        break;
      default:
        defaultValue = item.getWidgetDefault();
        break;
    }

    return defaultValue;
  }

  /**
   * 分割线样式
   *
   * @return
   */
  private JSONObject dividerStyle() {
    return JSONUtil.createObj()
        .putOpt("color", "#663300")
        .putOpt("font-size", "13pt")
        .putOpt("margin", "5px 0");
  }

  public boolean verifyVolume(VerifyDTO req, List<Volume> volumeList) {
    // 此处兼容预防部分传入表名为doc table_id
    req.setTableId(
        ObjectUtil.isNotEmpty(req.getTableId()) ? req.getTableId() : req.getDocTableId());
    checkCommonCmd.checkBaseVol(volumeList, req.getRefresh());
    // 分组规则
    Map<Integer, List<Volume>> docGroup =
        volumeList.stream().collect(Collectors.groupingBy(Volume::getYear));

    RuleFieldQueryDTO fieldQueryDTO = new RuleFieldQueryDTO();
    fieldQueryDTO.setArchTypeId(req.getArchTypeId());
    fieldQueryDTO.setTableId(req.getTableId());
    fieldQueryDTO.setYear(docGroup.keySet().iterator().next());
    Long projId = null;
    if (ObjectUtil.isNotEmpty(volumeList)) {

      projId = volumeList.get(0).getProjId();
    }
    setRuleByProjId(req.getArchTypeId(), projId, fieldQueryDTO);
    // 获取文件规则
    List<ArchiveRuleFieldListVO> lists = archiveRuleMainSvc.getRuleFieldByVersion(fieldQueryDTO);
    // 移除件号
    lists =
        lists.stream()
            .filter(item -> !item.getFieldCode().equalsIgnoreCase(ArchConst.VOL_SEQUENCE_DB))
            .collect(Collectors.toList());
    checkCommonCmd.checkRuleVol(lists, volumeList, req.getRefresh());
    return true;
  }

  public boolean verifyDocument(VerifyDTO req, List<Document> docList) {
    // 此处兼容预防部分传入表名为doc table_id
    req.setTableId(
        ObjectUtil.isNotEmpty(req.getTableId()) ? req.getTableId() : req.getDocTableId());
    // 判断年度是否一致
    checkCommonCmd.checkBaseDoc(docList, req.getRefresh());
    // 分组规则
    Map<Integer, List<Document>> docGroup =
        docList.stream().collect(Collectors.groupingBy(Document::getYear));

    RuleFieldQueryDTO fieldQueryDTO = new RuleFieldQueryDTO();
    fieldQueryDTO.setArchTypeId(req.getArchTypeId());
    fieldQueryDTO.setTableId(req.getTableId());
    fieldQueryDTO.setYear(docGroup.keySet().iterator().next());
    Long projId = null;
    if (ObjectUtil.isNotEmpty(docList)) {
      projId = docList.get(0).getProjId();
    }

    setRuleByProjId(req.getArchTypeId(), projId, fieldQueryDTO);
    // 获取文件规则
    List<ArchiveRuleFieldListVO> lists = archiveRuleMainSvc.getRuleFieldByVersion(fieldQueryDTO);
    // 移除件号
    lists =
        lists.stream()
            .filter(item -> !item.getFieldCode().equalsIgnoreCase(ArchConst.DOC_SEQUENCE_DB))
            .collect(Collectors.toList());
    checkCommonCmd.checkRuleDoc(lists, docList, req.getRefresh());
    return true;
  }

  @Override
  public boolean verify(VerifyDTO req) {
    if (StrUtil.isNotBlank(req.getVolIds())) {
      // 此处兼容预防部分传入表名为doc table_id
      req.setTableId(
          ObjectUtil.isNotEmpty(req.getVolTableId()) ? req.getVolTableId() : req.getTableId());
      // 判断年度是否一致
      DynamicTableNameUtil.setTableName(req.getTableId());
      List<Volume> volumeList = volumeSvc.selectListByIds(IdUtil.split(req.getVolIds()));
      verifyVolume(req, volumeList);
      return true;
    }

    if (StrUtil.isNotBlank(req.getDocIds())) {
      // 此处兼容预防部分传入表名为doc table_id
      req.setTableId(
          ObjectUtil.isNotEmpty(req.getTableId()) ? req.getTableId() : req.getDocTableId());
      // 判断年度是否一致
      List<Document> docList = documentSvc.selectListByIds(req);
      verifyDocument(req, docList);
      return true;
    }
    return true;
  }

  /**
   * 用于归档校验
   *
   * @param req
   * @return
   */
  public boolean verifyArchiving(VerifyDTO req) {
    if (StrUtil.isNotBlank(req.getVolIds())) {
      // 此处兼容预防部分传入表名为doc table_id
      req.setTableId(
          ObjectUtil.isNotEmpty(req.getVolTableId()) ? req.getVolTableId() : req.getTableId());
      // 判断年度是否一致
      DynamicTableNameUtil.setTableName(req.getTableId());
      List<Volume> volumeList = volumeSvc.selectListByIds(IdUtil.split(req.getVolIds()));
      verifyArchivingVolume(req, volumeList);
      return true;
    }

    if (StrUtil.isNotBlank(req.getDocIds())) {
      // 此处兼容预防部分传入表名为doc table_id
      req.setTableId(
          ObjectUtil.isNotEmpty(req.getTableId()) ? req.getTableId() : req.getDocTableId());
      // 判断年度是否一致
      List<Document> docList = documentSvc.selectListByIds(req);
      verifyArchivingDocument(req, docList);
      return true;
    }
    return true;
  }

  /**
   * 归档校验案卷
   *
   * @param req
   * @param volumeList
   * @return
   */
  public boolean verifyArchivingVolume(VerifyDTO req, List<Volume> volumeList) {
    // 此处兼容预防部分传入表名为doc table_id
    req.setTableId(
        ObjectUtil.isNotEmpty(req.getTableId()) ? req.getTableId() : req.getDocTableId());
    // 分组规则
    Map<Optional<Integer>, List<Volume>> docGroup =
        volumeList.stream()
            .collect(Collectors.groupingBy(vol -> Optional.ofNullable(vol.getYear())));

    RuleFieldQueryDTO fieldQueryDTO = new RuleFieldQueryDTO();
    fieldQueryDTO.setArchTypeId(req.getArchTypeId());
    fieldQueryDTO.setTableId(req.getTableId());
    Long projId = null;
    if (ObjectUtil.isNotEmpty(volumeList)) {

      projId = volumeList.get(0).getProjId();
    }
    setRuleByProjId(req.getArchTypeId(), projId, fieldQueryDTO);
    // 获取文件规则
    List<ArchiveRuleFieldListVO> lists = archiveRuleMainSvc.getRuleFieldByVersion(fieldQueryDTO);
    // 移除件号
    lists =
        lists.stream()
            .filter(item -> !item.getFieldCode().equalsIgnoreCase(ArchConst.VOL_SEQUENCE_DB))
            .collect(Collectors.toList());
    // 根据规则判空
    List<ArchiveRuleFieldListVO> finalLists = lists;
    // 项目内档案信息校验
    if (ObjectUtil.isNotEmpty(projId)) {
      checkListValue(req, volumeList, finalLists);
    } else {
      docGroup.forEach(
          (year, docList) -> {
            List<ArchiveRuleFieldListVO> ruleFieldList =
                archiveRuleMainSvc.getRuleFieldByVersion(fieldQueryDTO);
            checkListValue(req, docList, ruleFieldList);
          });
    }

    return true;
  }

  /**
   * 数据非空校验
   *
   * @param req
   * @param volumeList
   * @param finalLists
   */
  private void checkListValue(
      VerifyDTO req, List<Volume> volumeList, List<ArchiveRuleFieldListVO> finalLists) {
    volumeList.forEach(
        (item) -> {
          finalLists.stream()
              .filter(ArchiveRuleFieldListVO::getEnableGroup)
              .forEach(
                  rule -> {
                    String field =
                        !StringUtils.isCamel(rule.getFieldCode())
                            ? StringUtils.underlineToCamel(rule.getFieldCode())
                            : rule.getFieldCode();

                    if (req.getRefresh()
                        && (field.equalsIgnoreCase(ArchConst.VOL_SEQUENCE)
                            || field.equalsIgnoreCase(ArchConst.ARCH_CODE))) {
                      return;
                    }

                    Object value = checkCommonCmd.getValue(item, field);
                    if (ObjectUtil.isEmpty(value)) {
                      log.info("数据校验 {} 为空", field);
                      throw DataCheckException.instance(ErrorCode.NOT_FOUND, "当前所选【档案】不满足操作条件");
                    }
                  });
        });
  }

  /**
   * 归档校验文件
   *
   * @param req
   * @param docList
   * @return
   */
  public boolean verifyArchivingDocument(VerifyDTO req, List<Document> docList) {
    // 此处兼容预防部分传入表名为doc table_id
    req.setTableId(
        ObjectUtil.isNotEmpty(req.getTableId()) ? req.getTableId() : req.getDocTableId());
    // 分组规则
    Map<Integer, List<Document>> docGroup =
        docList.stream().collect(Collectors.groupingBy(Document::getYear));

    RuleFieldQueryDTO fieldQueryDTO = new RuleFieldQueryDTO();
    fieldQueryDTO.setArchTypeId(req.getArchTypeId());
    fieldQueryDTO.setTableId(req.getTableId());
    fieldQueryDTO.setYear(docGroup.keySet().iterator().next());
    Long projId = null;
    if (ObjectUtil.isNotEmpty(docList)) {
      projId = docList.get(0).getProjId();
    }

    setRuleByProjId(req.getArchTypeId(), projId, fieldQueryDTO);
    // 获取文件规则
    List<ArchiveRuleFieldListVO> lists = archiveRuleMainSvc.getRuleFieldByVersion(fieldQueryDTO);
    // 移除件号
    lists =
        lists.stream()
            .filter(item -> !item.getFieldCode().equalsIgnoreCase(ArchConst.DOC_SEQUENCE_DB))
            .collect(Collectors.toList());
    //    checkCommonCmd.checkRuleDoc(lists, docList, req.getRefresh());

    // 根据规则判空
    List<ArchiveRuleFieldListVO> finalLists = lists;
    docList.forEach(
        item -> {
          finalLists.stream()
              .filter(ArchiveRuleFieldListVO::getEnableGroup)
              .forEach(
                  rule -> {
                    String field =
                        !StringUtils.isCamel(rule.getFieldCode())
                            ? StringUtils.underlineToCamel(rule.getFieldCode())
                            : rule.getFieldCode();

                    if (req.getRefresh()
                        && (field.equalsIgnoreCase(ArchConst.DOC_SEQUENCE)
                            || field.equalsIgnoreCase(ArchConst.ARCH_CODE))) {
                      return;
                    }

                    Object value = checkCommonCmd.getValue(item, field);
                    if (ObjectUtil.isEmpty(value)) {
                      log.info("数据 {} 校验为空", field);
                      throw DataCheckException.instance(
                          ErrorCode.VALIDATE_NOT_NULL, "当前所选【档案】" + field + " 不满足操作条件");
                    }
                  });
        });
    return true;
  }

  @Override
  public CheckRepeatVO checkRepeat(Document req, Map<String, String> map) {
    CheckRepeatVO checkRepeatVO = new CheckRepeatVO();
    if (map.isEmpty()) {
      checkRepeatVO.setCount(0);
      return checkRepeatVO;
    }
    QueryWrapper<Document> wrapper = Wrappers.query();
    for (String key : map.keySet()) {
      wrapper.eq(key, map.get(key));
    }
    DynamicTableNameUtil.setTableName(req.getTableId());
    checkRepeatVO.setCount(documentSvc.count(wrapper));
    return checkRepeatVO;
  }

  @Override
  public LoadBoxVO loadBox(LoadBoxDTO req) {
    LoadBoxVO boxVO = LoadBoxVO.builder().build();
    // 1、查询固定内容 -- 2、中间拼接符 -- 3、查询最新流水号
    if (StrUtil.isBlank(req.getDocIds())) {
      throw BizTipException.instance(ErrorCode.NOT_SELECTED_DATA, "未选择数据");
    }
    Document qryDoc = new Document();
    qryDoc.setArchTypeId(req.getArchTypeId());
    qryDoc.setTableId(req.getDocTableId());
    qryDoc.setId(IdUtil.splitToList(req.getDocIds()).get(0));
    Document document = documentSvc.view(qryDoc);
    RuleFieldQueryDTO fieldQueryDTO = BeanUtil.copyProperties(req, RuleFieldQueryDTO.class);
    fieldQueryDTO.setHaveBox(true);
    setRuleByProjId(req.getArchTypeId(), req.getProjId(), fieldQueryDTO);
    ArchiveRuleMainListVO ruleMainListVO = archiveRuleMainSvc.getRuleMainByVersion(fieldQueryDTO);
    List<ArchiveRuleFieldListVO> lists = archiveRuleMainSvc.getRuleFieldByVersion(fieldQueryDTO);
    if (lists.isEmpty()) {
      return boxVO;
    }
    Map<String, Object> columnMap = new HashMap<>();
    // 生成拼接部分内容
    lists.stream()
        .filter(ArchiveRuleFieldListVO::getEnableBoxGroup)
        .filter(f -> !f.getFieldCode().contains(ArchConst.SEQUENSE))
        .forEach(
            item -> {
              if (!NumberUtil.equals(RuleFieldTypeEnum.FIXED.getCode(), item.getType())) {
                String value =
                    checkCommonCmd.getValue(
                        document,
                        !StringUtils.isCamel(item.getFieldCode())
                            ? StringUtils.underlineToCamel(item.getFieldCode())
                            : item.getFieldCode());
                if (StrUtil.isNotBlank(value)) {
                  columnMap.put(item.getFieldCode(), value);
                }
              }
            });
    // 判断动态获取
    DynamicTableNameUtil.setTableName(req.getBoxTableId());
    Integer sequence = boxSvc.getSequence(columnMap);
    boxVO.setBoxSequence(sequence + 1);
    // 获取流水部分内容
    boxVO.setBoxNo(IdUtil.autoGenCode(sequence, ruleMainListVO.getCodeLen()));
    log.debug("当前生成的盒号:{}", boxVO.getBoxSequence());
    boxVO.setCodeLen(ruleMainListVO.getCodeLen());
    // 加载默认盒宽度
    ArchiveTypeVO archiveTypeVO = archiveTypeSvc.view(req.getArchTypeId());
    boxVO.setVolBoxDefaultSize(archiveTypeVO.getVolBoxDefaultSize());
    return boxVO;
  }

  @Override
  public Page<Box> loadBoxList(Page reqPage, LoadBoxDTO req) {
    // 判断年度是否一致
    BaseBO baseBO = BeanUtil.copyProperties(req, BaseBO.class);
    baseBO.setDocIds(req.getDocIds());
    baseBO.setTableId(req.getDocTableId());
    List<Document> docList = documentSvc.selectListByIds(baseBO);
    // 分组规则
    Map<Integer, List<Document>> docGroup =
        docList.stream().collect(Collectors.groupingBy(Document::getYear));
    if (docGroup.keySet().size() != 1) {
      throw DataCheckException.instance(ErrorCode.DATA_NOT_IN_SAME_GROUP, "检测到条目的分组数据不满足规则！");
    }
    if (!docList.isEmpty() && BooleanUtil.isTrue(req.getSwitchBox())) {
      // 换盒当前盒号一定一致
      req.setSwitchBoxId(docList.get(0).getBoxId());
    }

    RuleFieldQueryDTO fieldQueryDTO = new RuleFieldQueryDTO();
    fieldQueryDTO.setArchTypeId(req.getArchTypeId());
    fieldQueryDTO.setTableId(req.getDocTableId());
    fieldQueryDTO.setYear(docGroup.keySet().iterator().next());
    fieldQueryDTO.setHaveBox(true);
    // 获取文件规则
    setRuleByProjId(req.getArchTypeId(), req.getProjId(), fieldQueryDTO);
    List<ArchiveRuleFieldListVO> lists = archiveRuleMainSvc.getRuleFieldByVersion(fieldQueryDTO);
    Map<String, Object> columnMap = new HashMap<>();
    // 生成拼接部分内容
    lists.stream()
        .filter(ArchiveRuleFieldListVO::getEnableBoxGroup)
        .filter(f -> !f.getFieldCode().contains(ArchConst.SEQUENSE))
        .filter(ArchiveRuleFieldListVO::getEnableGroup)
        .forEach(
            item -> {
              if (!NumberUtil.equals(RuleFieldTypeEnum.FIXED.getCode(), item.getType())) {
                String value =
                    checkCommonCmd.getValue(
                        docList.get(0),
                        !StringUtils.isCamel(item.getFieldCode())
                            ? StringUtils.underlineToCamel(item.getFieldCode())
                            : item.getFieldCode());
                if (StrUtil.isNotBlank(value)) {
                  columnMap.put(item.getFieldCode(), value);
                }
              }
            });
    DynamicTableNameUtil.setTableName(req.getBoxTableId());
    if (ObjectUtil.isNotEmpty(req.getStatus())) {
      columnMap.put(ArchConst.DEF_STATUS, req.getStatus());
    }
    // 判断动态获取
    return boxSvc.getBoxByGroup(reqPage, columnMap, req);
  }

  @Override
  public List<InBoxVO> inBox(InBoxDTO req) {
    // 判断是否修改过新盒号
    if (ObjectUtil.isNotEmpty(req.getNewBoxSequence())) {
      req.setBoxSequence(req.getNewBoxSequence());
      req.setBoxNo(IdUtil.autoGenCode(req.getNewBoxSequence(), req.getCodeLen(), false));
      // 校验盒号是否重复
      RuleFieldQueryDTO fieldQueryDTO = BeanUtil.copyProperties(req, RuleFieldQueryDTO.class);
      fieldQueryDTO.setHaveBox(true);

      Set<Long> docIds = IdUtil.split(req.getDocIds());
      if (ObjectUtil.isNotEmpty(docIds)) {
        Document qryDoc = new Document();
        qryDoc.setArchTypeId(req.getArchTypeId());
        qryDoc.setTableId(req.getDocTableId());
        Document document = Optional.ofNullable(documentSvc.view(qryDoc)).orElse(new Document());
        setRuleByProjId(req.getArchTypeId(), document.getProjId(), fieldQueryDTO);
      }

      List<ArchiveRuleFieldListVO> lists = archiveRuleMainSvc.getRuleFieldByVersion(fieldQueryDTO);
      if (lists.isEmpty()) {
        throw BizTipException.instance(ErrorCode.NOT_FOUND, "未找到装盒规则！");
      }
      Map<String, Object> columnMap = new HashMap<>();
      // 生成拼接部分内容
      lists.forEach(
          item -> {
            if (!NumberUtil.equals(RuleFieldTypeEnum.FIXED.getCode(), item.getType())) {
              String value =
                  checkCommonCmd.getValue(
                      req,
                      !StringUtils.isCamel(item.getFieldCode())
                          ? StringUtils.underlineToCamel(item.getFieldCode())
                          : item.getFieldCode());
              if (StrUtil.isNotBlank(value)) {
                columnMap.put(item.getFieldCode(), value);
              }
            }
          });
      // 判断动态获取
      DynamicTableNameUtil.setTableName(req.getBoxTableId());
      boolean flag = boxSvc.checkBoxSequence(columnMap, req.getBoxSequence());
      if (!flag) {
        throw BizTipException.instance(ErrorCode.NOT_FOUND, "当前盒号已经存在！");
      }
    }
    return inBoxCmd.inBox(req);
  }

  @Override
  public ArchCodeVO archCode(Document req) {
    return archCodeGenCmd.archCode(req);
  }

  @Override
  public ArchCodeVO archCode(Volume req) {
    return archCodeGenCmd.archCode(req);
  }

  @Override
  public boolean refreshArchCode(RefreshArchCodeDTO req) {
    boolean flag = false;
    // 处理案卷级
    if (StrUtil.isNotBlank(req.getVolIds())) {
      refreshVolumeArchCode(req);
    }
    // 处理文件
    if (StrUtil.isNotBlank(req.getDocIds())) {
      refreshDocumentArchCode(req);
    }
    return flag;
  }

  public void refreshVolumeArchCode(RefreshArchCodeDTO req) {
    boolean flag = false;
    // 处理文件级
    VerifyDTO verifyDTO = BeanUtil.copyProperties(req, VerifyDTO.class);
    verifyDTO.setRefresh(true);
    // 刷新档号校验年度信息, 每次刷新档号选择数据分组信息一致
    // 进行前置校验
    DynamicTableNameUtil.setTableName(req.getTableId());
    List<Volume> volList = volumeSvc.selectListByIds(IdUtil.split(req.getVolIds()));
    if (ObjectUtil.isEmpty(volList)) {
      return;
    }
    // 排序
    //    verifyVolume(verifyDTO, volList);

    // 先按项目分组,区分
    Map<Optional<Long>, List<Volume>> projGroup =
        volList.stream()
            .collect(Collectors.groupingBy(item -> Optional.ofNullable(item.getProjId())));
    List<Volume> newArchCodeList =
        projGroup.entrySet().stream()
            .map(
                (entry) -> {
                  Long projId = null;
                  Optional<Long> key = entry.getKey();
                  List<Volume> groupList = entry.getValue();
                  if (key.isPresent()) {
                    projId = key.get();
                  }
                  if (ObjectUtil.isNull(projId)) {
                    req.setProjId(projId);
                    // 项目类型刷新档号
                    // 数据排序
                    return (List<Volume>) buildRefreshArchList(req, null, groupList);
                  } else {
                    // 非项目类型
                    List<Volume> noProjList = entry.getValue();
                    // 非项目档案,使用年度进行分组刷新档案,(处理整理规则多版本问题)
                    Map<Optional<Integer>, List<Volume>> yearGroup =
                        noProjList.stream()
                            .collect(
                                Collectors.groupingBy(item -> Optional.ofNullable(item.getYear())));
                    return yearGroup.entrySet().stream()
                        .map(
                            yearEntry -> {
                              req.setProjId(null);
                              Integer year = null;
                              Optional<Integer> yearKey = yearEntry.getKey();
                              if (yearKey.isPresent()) {
                                year = yearKey.get();
                              }
                              List<Volume> yearGroupList = yearEntry.getValue();
                              return (List<Volume>) buildRefreshArchList(req, year, yearGroupList);
                            })
                        .flatMap(Collection::stream)
                        .collect(Collectors.toList());
                  }
                })
            .flatMap(Collection::stream)
            .collect(Collectors.toList());
    DynamicTableNameUtil.setTableName(req.getTableId());
    volumeSvc.updateBatchById(newArchCodeList);
    // 添加档案管理日志
    Volume eventDoc = new Volume();
    eventDoc.setArchTypeId(req.getArchTypeId());
    eventDoc.setTableId(req.getTableId());
    eventDoc.setDocIds(
        newArchCodeList.stream()
            .map(item -> String.valueOf(item.getId()))
            .collect(Collectors.joining(",")));
    amsEventPublisher.publishEvent(
        new VolumeLogEvent(eventDoc, ArchOpEnum.UPDATE_ARCH_CODE, "更新档号"));
    amsEventPublisher.publishEvent(
        new ChangeInVolumeArchCodeEvent(
            ChangeInVolumeArchCodeDTO.builder()
                .tableId(req.getTableId())
                .archTypeId(req.getArchTypeId())
                .volIds(req.getVolIds())
                .projId(req.getProjId())
                .build()));
  }

  /**
   * 文书更新档号
   *
   * @param req
   */
  public void refreshDocumentArchCode(RefreshArchCodeDTO req) {
    // 处理文件
    VerifyDTO verifyDTO = BeanUtil.copyProperties(req, VerifyDTO.class);
    verifyDTO.setRefresh(true);
    // 进行前置校验
    List<Document> listDocs = documentSvc.selectListByIds(req);
    if (ObjectUtil.isEmpty(listDocs)) {
      return;
    }
    // 不再校验分组信息及年度
    //    verifyDocument(verifyDTO, listDocs);
    // 先按项目分组,区分
    Map<Optional<Long>, List<Document>> projGroup =
        listDocs.stream()
            .collect(Collectors.groupingBy(item -> Optional.ofNullable(item.getProjId())));
    List<Document> newArchCodeList =
        projGroup.entrySet().stream()
            .map(
                (entry) -> {
                  Optional<Long> key = entry.getKey();
                  if (key.isPresent()) {
                    Long projId = key.get();
                    List<Document> groupList = entry.getValue();
                    req.setProjId(projId);
                    // 项目类型刷新档号
                    // 数据排序
                    return (List<Document>) buildRefreshArchList(req, null, groupList);

                  } else {
                    // 非项目类型
                    List<Document> noProjList = entry.getValue();
                    // 非项目档案,使用年度进行分组刷新档案,(处理整理规则多版本问题)
                    Map<Optional<Integer>, List<Document>> yearGroup =
                        noProjList.stream()
                            .collect(
                                Collectors.groupingBy(item -> Optional.ofNullable(item.getYear())));
                    return yearGroup.entrySet().stream()
                        .map(
                            yearEntry -> {
                              req.setProjId(null);
                              Integer year = null;
                              Optional<Integer> yearKey = yearEntry.getKey();
                              if (yearKey.isPresent()) {
                                year = yearKey.get();
                              }
                              List<Document> yearGroupList = yearEntry.getValue();
                              return (List<Document>)
                                  buildRefreshArchList(req, year, yearGroupList);
                            })
                        .flatMap(Collection::stream)
                        .collect(Collectors.toList());
                  }
                })
            .flatMap(Collection::stream)
            .collect(Collectors.toList());
    DynamicTableNameUtil.setTableName(req.getTableId());
    documentSvc.updateBatchById(newArchCodeList);
    // 添加档案管理日志
    Document eventDoc = new Document();
    eventDoc.setArchTypeId(req.getArchTypeId());
    eventDoc.setTableId(req.getTableId());
    eventDoc.setDocIds(
        newArchCodeList.stream()
            .map(item -> String.valueOf(item.getId()))
            .collect(Collectors.joining(",")));
    amsEventPublisher.publishEvent(
        new DocumentLogEvent(eventDoc, ArchOpEnum.UPDATE_ARCH_CODE, "更新档号"));
  }

  private List<? extends SystemBO> buildRefreshArchList(
      RefreshArchCodeDTO req, Integer year, List<? extends SystemBO> groupList) {
    List<ArchiveOrderFieldListVO> orderFieldList =
        getArchiveOrderFieldList(req.getArchTypeId(), req.getTableId());
    if (ObjectUtil.isNotEmpty(orderFieldList)) {
      List<SortUtils.FieldOrder> fieldOrders =
          ConvertUtil.toList(orderFieldList, SortUtils.FieldOrder.class);
      groupList.sort(SortUtils.buildSort(fieldOrders));
    }
    return com.google.common.collect.Lists.newArrayList();
  }

  @NotNull
  private List<ArchiveRuleFieldListVO> getArchiveRuleFieldList(
      RefreshArchCodeDTO req, Integer year) {
    ArchiveRuleMain ruleInfo = getRuleInfo(req.getArchTypeId(), req.getProjId(), year);
    RuleFieldQueryDTO ruleFieldQueryDTO = new RuleFieldQueryDTO();
    ruleFieldQueryDTO.setArchTypeId(req.getArchTypeId());
    ruleFieldQueryDTO.setRuleId(ruleInfo.getId());

    List<ArchiveRuleFieldListVO> ruleFieldList =
        archiveRuleMainSvc.getRuleFieldByVersion(ruleFieldQueryDTO);
    if (ObjectUtil.isEmpty(ruleFieldList)) {
      throw BizTipException.instance(ErrorCode.NOT_FOUND, "未找到规则信息");
    }
    // 按分组字段条件，直接将数据分组， stream 嵌套group 会有展开的问题，如 Map<year,Map<classfy,List<Document>>>
    // 最佳方案，key为 分组信息，value为分好组的数据信息 Map<key,List<Document>>
    // 存在不符合分组规则的数据，直接返回前端错误数据信息
    return ruleFieldList.stream()
        .filter(f -> ObjectUtil.equals(f.getTableId(), req.getTableId()))
        .filter(f -> !f.getFieldCode().contains(ArchConst.SEQUENSE))
        .filter(ArchiveRuleFieldListVO::getEnableGroup)
        .collect(Collectors.toList());
  }

  private List<ArchiveOrderFieldListVO> getArchiveOrderFieldList(Long archTypeId, String tableId) {
    ArchiveOrderFieldQueryDTO qry = new ArchiveOrderFieldQueryDTO();
    qry.setArchTypeId(archTypeId);
    qry.setTableId(tableId);
    return archiveOrderFieldSvc.selectList(qry);
  }

  public ArchiveRuleMain getRuleInfo(Long archTypeId, Long projId, Integer year) {
    // 项目下案卷，整理规则以项目整理规则为准
    if (Objects.nonNull(projId)) {
      Project qry = new Project();
      qry.setId(projId);
      ArchiveTypeVO archInfo =
          Optional.ofNullable(archiveTypeSvc.view(archTypeId))
              .orElseThrow(() -> BizTipException.instance(ErrorCode.NOT_FOUND, "未找到门类信息"));
      String projTableId = archInfo.getProjTableId();
      qry.setTableId(projTableId);
      Project project =
          Optional.ofNullable(projectSvc.view(qry))
              .orElseThrow(() -> BizTipException.instance(ErrorCode.NOT_FOUND, "未找到项目信息"));
      Long archRuleId = project.getArchRuleId();
      return archiveRuleMainSvc.getById(archRuleId);
    }
    // 普通案卷
    RuleFieldQueryDTO qryRuleMain = new RuleFieldQueryDTO();
    qryRuleMain.setYear(year);
    qryRuleMain.setArchTypeId(archTypeId);
    ArchiveRuleMainListVO ruleMainByVersion = archiveRuleMainSvc.getRuleMainByVersion(qryRuleMain);
    return ConvertUtil.toBean(ruleMainByVersion, ArchiveRuleMain.class);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public boolean archiving(ArchivingDTO req) {
    boolean flag = false;
    List<Document> listDocs = Lists.newArrayList();
    // 处理文件
    if (StrUtil.isNotBlank(req.getDocIds())) {
      req.setTableId(req.getDocTableId());
      listDocs = documentSvc.selectListByIds(req);
    }
    // 处理盒
    if (StrUtil.isNotBlank(req.getBoxIds())) {
      DynamicTableNameUtil.setTableName(req.getBoxTableId());
      Set<Long> boxIds = IdUtil.split(req.getBoxIds());
      List<Box> boxList = boxSvc.selectListByIds(boxIds);
      archivingCmd.checkRepeat(ArchStatusEnum.S1, req.getArchTypeId(), req.getTableId(), boxList);
      List<Box> batchBox =
          boxList.stream()
              .peek(
                  item -> {
                    item.setArchTypeId(req.getArchTypeId());
                    item.setTableId(req.getTableId());
                    item.setStatus(ArchStatusEnum.S1.getCode());
                    item.setDepartmentId(
                        ObjUtil.isEmpty(item.getDepartmentId())
                            ? SecurityUtil.getLoginUser().getDeptId()
                            : item.getDepartmentId());
                    item.setDepartment(
                        ObjUtil.isEmpty(item.getDepartment())
                            ? SecurityUtil.getLoginUser().getDeptName()
                            : item.getDepartment());
                    item.setFilling(
                        ObjUtil.isEmpty(item.getFilling())
                            ? SecurityUtil.getUserId().toString()
                            : item.getFilling());
                  })
              .collect(Collectors.toList());
      flag = boxSvc.updateBatchById(batchBox);
      req.setTableId(req.getDocTableId());
      listDocs = documentSvc.selectListByBoxIds(req, true);
    }
    // 处理案卷
    if (StrUtil.isNotBlank(req.getVolIds())) {
      DynamicTableNameUtil.setTableName(req.getVolTableId());
      Set<Long> volIds = IdUtil.split(req.getVolIds());
      List<Volume> volumeList = volumeSvc.selectListByIds(volIds);

      archivingCmd.checkRepeat(
          ArchStatusEnum.S1, req.getArchTypeId(), req.getTableId(), volumeList);
      List<Volume> batchVol =
          volumeList.stream()
              .peek(
                  item -> {
                    item.setArchTypeId(req.getArchTypeId());
                    item.setTableId(req.getTableId());
                    item.setStatus(ArchStatusEnum.S1.getCode());
                    try {
                      item.setDepartmentId(
                          ObjUtil.isEmpty(item.getDepartmentId())
                              ? SecurityUtil.getLoginUser().getDeptId()
                              : item.getDepartmentId());
                      item.setDepartment(
                          ObjUtil.isEmpty(item.getDepartment())
                              ? SecurityUtil.getLoginUser().getDeptName()
                              : item.getDepartment());
                      item.setFilling(
                          ObjUtil.isEmpty(item.getFilling())
                              ? SecurityUtil.getUserId().toString()
                              : item.getFilling());
                      item.setFillingDepartment(
                          ObjUtil.isEmpty(item.getFillingDepartment())
                              ? SecurityUtil.getLoginUser().getDeptName()
                              : item.getFillingDepartment());

                      // 设置归档日期，归档人，归档份数
                      item.setFillingPerson(
                          ObjUtil.isEmpty(item.getFillingPerson())
                              ? SecurityUtil.getLoginUser().getRealName()
                              : item.getFillingPerson());
                      item.setFillingDate(
                          ObjUtil.isEmpty(item.getFillingDate())
                              ? DateUtil.format(new Date(), DatePattern.PURE_DATE_FORMAT)
                              : item.getFillingDate());
                      item.setFillingNum(
                          ObjUtil.isEmpty(item.getFillingNum()) ? 1 : item.getFillingNum());
                      // 判断成文日期，计算到期时间
                      if (ObjectUtil.isNotEmpty(item.getStartDate())
                          && ObjectUtil.isEmpty(item.getExpireYear())) {
                        item.setExpireYear(
                            retentionCmd.getExpireYear(
                                item.getYear(), item.getRetentionPeriod(), item.getStartDate()));
                      }
                    } catch (Exception ex) {
                      log.debug("设置部分数据异常，允许归档！");
                    }
                  })
              .collect(Collectors.toList());
      flag = volumeSvc.updateBatchById(batchVol);
      req.setTableId(req.getDocTableId());
      listDocs = documentSvc.selectListByVolIds(req, true);
    }

    List<Long> ids = Lists.newArrayList();
    archivingCmd.checkRepeat(ArchStatusEnum.S1, req.getArchTypeId(), req.getTableId(), listDocs);
    List<Document> docBatch =
        listDocs.stream()
            .peek(
                item -> {
                  ids.add(item.getId());
                  item.setArchTypeId(req.getArchTypeId());
                  item.setTableId(req.getTableId());
                  item.setStatus(ArchStatusEnum.S1.getCode());
                  item.setDepartmentId(
                      ObjUtil.isEmpty(item.getDepartmentId())
                          ? SecurityUtil.getLoginUser().getDeptId()
                          : item.getDepartmentId());
                  item.setDepartment(
                      ObjUtil.isEmpty(item.getDepartment())
                          ? SecurityUtil.getLoginUser().getDeptName()
                          : item.getDepartment());
                  item.setFilling(
                      ObjUtil.isEmpty(item.getFilling())
                          ? SecurityUtil.getUserId().toString()
                          : item.getFilling());
                  item.setFillingDepartment(
                      ObjUtil.isEmpty(item.getFillingDepartment())
                          ? SecurityUtil.getLoginUser().getDeptName()
                          : item.getFillingDepartment());

                  // 设置归档日期，归档人，归档份数
                  item.setFillingPerson(
                      ObjUtil.isEmpty(item.getFillingPerson())
                          ? SecurityUtil.getLoginUser().getRealName()
                          : item.getFillingPerson());
                  item.setFillingDate(
                      ObjUtil.isEmpty(item.getFillingDate()) ? new Date() : item.getFillingDate());
                  item.setFillingNum(
                      ObjUtil.isEmpty(item.getFillingNum()) ? 1 : item.getFillingNum());
                  // 判断成文日期，计算到期时间
                  if (ObjectUtil.isNotEmpty(item.getDocDate())) {
                    item.setExpireYear(
                        retentionCmd.getExpireYear(
                            item.getYear(), item.getRetentionPeriod(), item.getDocDate()));
                  }
                })
            .collect(Collectors.toList());
    // 归档前进行文件基础校验
    req.setDocIds(StrUtil.join(",", ids));
    verifyArchiving(BeanUtil.copyProperties(req, VerifyDTO.class));
    // 执行最后修改
    DynamicTableNameUtil.setTableName(req.getDocTableId());
    flag = documentSvc.updateBatchById(docBatch);

    return flag;
  }

  @Override
  public boolean againCollect(AgainCollectDTO req) {
    boolean flag = false;
    List<Document> listDocs = Lists.newArrayList();
    // 处理文件
    if (StrUtil.isNotBlank(req.getDocIds())) {
      listDocs = documentSvc.selectListByIds(req);
    }
    // 处理盒
    if (StrUtil.isNotBlank(req.getBoxIds())) {
      List<Box> boxList = boxSvc.selectListByIds(IdUtil.split(req.getBoxIds()));
      List<Box> batchBox =
          boxList.stream()
              .peek(
                  item -> {
                    item.setArchTypeId(req.getArchTypeId());
                    item.setTableId(req.getTableId());
                    item.setStatus(ArchStatusEnum.S0.getCode());
                  })
              .collect(Collectors.toList());
      flag = boxSvc.updateBatchById(batchBox);
      req.setTableId(req.getDocTableId());
      listDocs = documentSvc.selectListByBoxIds(req, true);
    }
    // 处理案卷
    if (StrUtil.isNotBlank(req.getVolIds())) {
      List<Volume> volumeList = volumeSvc.selectListByIds(IdUtil.split(req.getVolIds()));
      List<Volume> batchVol =
          volumeList.stream()
              .peek(
                  item -> {
                    item.setArchTypeId(req.getArchTypeId());
                    item.setTableId(req.getTableId());
                    item.setStatus(ArchStatusEnum.S0.getCode());
                  })
              .collect(Collectors.toList());
      flag = volumeSvc.updateBatchById(batchVol);
      req.setTableId(req.getDocTableId());
      listDocs = documentSvc.selectListByVolIds(req, true);
    }

    List<Document> docBatch =
        listDocs.stream()
            .peek(
                item -> {
                  item.setArchTypeId(req.getArchTypeId());
                  item.setTableId(req.getTableId());
                  item.setStatus(ArchStatusEnum.S0.getCode());
                })
            .collect(Collectors.toList());
    // 执行最后修改
    DynamicTableNameUtil.setTableName(
        StrUtil.isNotBlank(req.getDocTableId()) ? req.getDocTableId() : req.getTableId());
    flag = documentSvc.updateBatchById(docBatch);
    return flag;
  }

  @Override
  public boolean adjustGroup(AdjustGroupDTO req) {
    boolean flag = false;
    // 处理文件
    if (StrUtil.isNotBlank(req.getDocIds())) {
      BaseBO baseBO = BeanUtil.copyProperties(req, BaseBO.class);
      baseBO.setDocIds(req.getDocIds());
      List<Document> listDocs = documentSvc.selectListByIds(baseBO);
      List<Document> docBatch =
          listDocs.stream()
              .peek(
                  item -> {
                    item.setArchTypeId(req.getArchTypeId());
                    item.setTableId(req.getTableId());
                    item.setClassfy(req.getClassfy());
                    item.setClassfyName(req.getClassfyName());
                    item.setYear(req.getYear());
                    item.setRetentionPeriod(req.getRetentionPeriod());
                  })
              .collect(Collectors.toList());
      flag = documentSvc.updateBatchById(docBatch);
    }

    // 处理盒
    if (StrUtil.isNotBlank(req.getBoxIds())) {
      List<Box> listDocs = boxSvc.selectListByIds(IdUtil.split(req.getBoxIds()));
      List<Box> docBatch =
          listDocs.stream()
              .peek(
                  item -> {
                    item.setArchTypeId(req.getArchTypeId());
                    item.setTableId(req.getTableId());
                    item.setClassfy(req.getClassfy());
                    item.setClassfyName(req.getClassfyName());
                    item.setYear(req.getYear());
                    item.setRetentionPeriod(req.getRetentionPeriod());
                  })
              .collect(Collectors.toList());
      flag = boxSvc.updateBatchById(docBatch);
    }

    return flag;
  }

  @Override
  public boolean updateField(UpdateFieldDTO req) {
    if (req.getTableId().contains(ArchTmplBO.DOCUMENT)) {
      return updateDocumentField(req);
    } else {
      return updateVolumeField(req);
    }
  }

  public boolean updateDocumentField(UpdateFieldDTO req) {
    boolean flag = false;
    List<Document> listDocs = Lists.newArrayList();
    if (BooleanUtil.isTrue(req.getBatchEditAll())) {
      Document docReq = BeanUtil.copyProperties(req, Document.class);
      listDocs = documentSvc.selectList(docReq);
    } else {
      BaseBO baseBO = BeanUtil.copyProperties(req, BaseBO.class);
      baseBO.setDocIds(req.getDocIds());
      listDocs = documentSvc.selectListByIds(baseBO);
    }
    List<Document> docBatch = Lists.newArrayList();
    listDocs.stream()
        .peek(
            item -> {
              docBatch.add(setFieldValue(item, req, Document.class));
              // 判断成文日期，计算到期时间
              if (ObjectUtil.isNotEmpty(item.getDocDate())) {
                item.setExpireYear(
                    retentionCmd.getExpireYear(
                        item.getYear(), item.getRetentionPeriod(), item.getDocDate()));
              }
            })
        .collect(Collectors.toList());
    flag = documentSvc.updateBatchById(docBatch);
    return flag;
  }

  public boolean updateVolumeField(UpdateFieldDTO req) {
    boolean flag = false;
    List<Volume> listDocs = Lists.newArrayList();
    if (BooleanUtil.isTrue(req.getBatchEditAll())) {
      Volume docReq = BeanUtil.copyProperties(req, Volume.class);
      listDocs = volumeSvc.selectList(docReq);
    } else {
      BaseBO baseBO = BeanUtil.copyProperties(req, BaseBO.class);
      baseBO.setDocIds(req.getDocIds());
      listDocs = volumeSvc.selectListByIds(IdUtil.split(baseBO.getVolIds()));
    }
    List<Volume> docBatch = Lists.newArrayList();
    listDocs.stream()
        .peek(
            item -> {
              docBatch.add(setFieldValue(item, req, Volume.class));
              // 判断成文日期，计算到期时间
              if (ObjectUtil.isNotEmpty(item.getStartDate())
                  && ObjectUtil.isEmpty(item.getExpireYear())) {
                item.setExpireYear(
                    retentionCmd.getExpireYear(
                        item.getYear(), item.getRetentionPeriod(), item.getStartDate()));
              }
            })
        .collect(Collectors.toList());
    flag = volumeSvc.updateBatchById(docBatch);
    // 异步添加触发案卷汇总

    CompletableFuture.runAsync(
        () -> {
          docBatch.forEach(
              item -> {
                item.setArchTypeId(req.getArchTypeId());
                item.setTableId(req.getTableId());
                volumeSvc.summary(item);
              });
        });

    return flag;
  }
  /**
   * 根据字段设置需要替换的值
   *
   * @param item
   * @param req
   * @return
   */
  private <T> T setFieldValue(T item, UpdateFieldDTO req, Class<T> clazz) {
    JSONObject jsonObject = JSONUtil.parseObj(item);
    StringBuffer sb = new StringBuffer();
    List<String> sts = StrUtil.split(req.getRules(), ",");
    sts.forEach(
        str -> {
          if (StrUtil.isBlank(str)) {
            sb.append(str);
          }
          // 判断是否是自定义输入
          else if (str.endsWith(Constants.EDIT_FIELD_INPUT)) {
            sb.append(str.replace(Constants.EDIT_FIELD_INPUT, ""));
          } else {
            sb.append(checkCommonCmd.getValue(item, optStr(str)));
          }
        });
    String value =
        StrUtil.contains(sb.toString(), "null")
            ? null
            : ObjectUtil.isEmpty(sb.toString()) ? null : sb.toString();

    String oldValue = checkCommonCmd.getValue(item, optStr(req.getEditField()));
    if (NumberUtil.equals(req.getReplaceMode(), 1)) {
      // 模式1全替换
      jsonObject.set(optStr(req.getEditField()), ObjectUtil.isEmpty(value) ? null : value);
    } else if (NumberUtil.equals(req.getReplaceMode(), 2)) {
      // 模式2，查找指定内容替换,此处默认从1开始，0为计算机开始值
      value =
          StrUtil.replace(
              oldValue,
              req.getReplaceStart() + 1,
              req.getReplaceStart() + 1 + req.getReplaceLength(),
              value);
      jsonObject.set(optStr(req.getEditField()), ObjectUtil.isEmpty(value) ? null : value);
    } else if (NumberUtil.equals(req.getReplaceMode(), 3)) {
      int i = StrUtil.indexOfIgnoreCase(oldValue, req.getReplaceSearch());
      value = StrUtil.replace(oldValue, i, i + req.getReplaceSearch().length(), value);
      jsonObject.set(optStr(req.getEditField()), ObjectUtil.isEmpty(value) ? null : value);
    }

    return JSONUtil.toBean(jsonObject, clazz);
  }

  /**
   * 判断需要处理的字段格式
   *
   * @param str 全宗号:(fondsCode)
   * @return
   */
  private String optStr(String str) {
    List<String> strs = StrUtil.split(str, ":");
    try {
      if (strs.size() == 2) {
        String str2 = strs.get(1);
        return str2.substring(1, str2.length() - 1);
      }
    } catch (Exception e) {
      log.debug("字符分隔异常！{}", e.getMessage());
    }
    return str;
  }

  @Override
  public List<InVolumeVO> inVolume(VolumeBuildUpDTO req) {
    return inVolumeCmd.inVolume(req);
  }

  @Override
  public List<ArchiveRuleFieldListVO> boxGroupFieldList(BoxGroupFieldQueryDTO req) {

    ArchiveRuleFieldQueryDTO qryRule = ConvertUtil.toBean(req, ArchiveRuleFieldQueryDTO.class);
    qryRule.setType(RuleFieldTypeEnum.METADATA.getCode());
    List<ArchiveRuleFieldListVO> fieldList = archiveRuleFieldSvc.selectList(qryRule);
    if (ObjectUtil.isEmpty(fieldList)) {
      return Lists.newArrayList();
    }
    Set<String> collect =
        fieldList.stream().map(ArchiveRuleFieldListVO::getFieldCode).collect(Collectors.toSet());
    ArchiveFieldQueryDTO qry = new ArchiveFieldQueryDTO();
    qry.setTableId(req.getBoxTableId());
    qry.setAttrUse(Boolean.TRUE);
    List<ArchiveFieldListVO> archFieldList = archiveFieldSvc.selectList(qry);
    Set<String> existKeyList =
        archFieldList.stream()
            .map(ArchiveFieldListVO::getDbFieldKey)
            .filter(collect::contains)
            .collect(Collectors.toSet());
    return fieldList.stream()
        .filter(f -> existKeyList.contains(f.getFieldCode()))
        .collect(Collectors.toList());
  }

  @Override
  public List<ArchiveRuleFieldListVO> getRuleFieldByVersion(RuleFieldQueryDTO req) {
    List<ArchiveRuleFieldListVO> fieldList = archiveRuleMainSvc.getRuleFieldByVersion(req);
    if (ObjectUtil.isEmpty(fieldList)) {
      return Lists.newArrayList();
    }
    fieldList.forEach(
        item -> {
          item.setFieldCode(StringUtils.underlineToCamel(item.getFieldCode()));
        });
    return fieldList;
  }

  @Override
  public void breakUp(BreakUpDTO req) {
    breakUpCmd.breakUp(req);
  }

  @Override
  public void mergeDocument(MergeDocumentDTO req) {
    mergeDocumentCmd.mergeDocument(req);
  }

  @Override
  public List<EfileRecordListVO> getMergeFileList(MergeEFileQueryDTO req) {
    return mergeDocumentCmd.getMergeFileList(req);
  }

  @Override
  public void changeSort(BaseBO req) {
    changeSortCmd.changeSort(req);
  }

  @Override
  public DocumentChangeSortListVO changeSortList(Document req) {
    return changeSortCmd.changeSortList(req);
  }

  @Override
  public VolumeChangeSortListVO changeSortList(Volume req) {
    return changeSortCmd.changeSortList(req);
  }

  @Override
  public void associatedWithData(AssociatedWithDataDTO req) {
    associatedFilesCmd.associatedWithData(req);
  }

  @Override
  public void associatedWithMeta(AssociatedWithMetaDTO req) {
    associatedFilesCmd.associatedWithMeta(req);
  }

  @Override
  public List<Tree<String>> selectArchTree(QueryArchTypeDTO req) {
    ArchiveTypeQueryDTO queryDTO = BeanUtil.copyProperties(req, ArchiveTypeQueryDTO.class);
    queryDTO.setId(req.getArchTypeId());
    // 查询当前全宗下档案库
    List<ArchiveTypeListVO> list = archiveTypeSvc.selectList(queryDTO);
    List finalTreeNodeList = Lists.newArrayList();
    List treeNodeList =
        list.stream()
            .map(
                item -> {
                  if (ObjectUtil.isNotEmpty(item.getDocTableId())) {
                    TreeNode docNode = new TreeNode();
                    docNode.setId(item.getDocTableId());
                    docNode.setName("文件");
                    docNode.setParentId(item.getId().toString());
                    docNode.setWeight(item.getSort());
                    docNode.setExtra(
                        ImmutableMap.of(
                            "dataCondition",
                            item.getDocTableId(),
                            "type",
                            "tableId",
                            "manageModel",
                            item.getManageModel()));
                    finalTreeNodeList.add(docNode);
                  }
                  if (item.getHaveBox()) {
                    TreeNode boxNode = new TreeNode();
                    boxNode.setId(item.getBoxTableId());
                    boxNode.setName("盒");
                    boxNode.setParentId(item.getId().toString());
                    boxNode.setWeight(item.getSort());
                    boxNode.setExtra(
                        ImmutableMap.of(
                            "dataCondition",
                            item.getBoxTableId(),
                            "type",
                            "tableId",
                            "manageModel",
                            item.getManageModel()));
                    finalTreeNodeList.add(boxNode);
                  }
                  if (item.getHaveVol()) {
                    TreeNode volNode = new TreeNode();
                    volNode.setId(item.getVolTableId());
                    volNode.setName("案卷");
                    volNode.setParentId(item.getId().toString());
                    volNode.setWeight(item.getSort());
                    volNode.setExtra(
                        ImmutableMap.of(
                            "dataCondition",
                            item.getVolTableId(),
                            "type",
                            "tableId",
                            "manageModel",
                            item.getManageModel()));
                    finalTreeNodeList.add(volNode);
                  }
                  if (item.getHaveProject()) {
                    TreeNode projNode = new TreeNode();
                    projNode.setId(item.getProjTableId());
                    projNode.setName("项目");
                    projNode.setParentId(item.getId().toString());
                    projNode.setWeight(item.getSort());
                    projNode.setExtra(
                        ImmutableMap.of(
                            "dataCondition",
                            item.getProjTableId(),
                            "type",
                            "tableId",
                            "manageModel",
                            item.getManageModel()));
                    finalTreeNodeList.add(projNode);
                  }
                  TreeNode treeNode = new TreeNode();
                  treeNode.setId(item.getId().toString());
                  treeNode.setName(item.getName());
                  treeNode.setParentId(Constants.DEFAULT_TOP_TREE_PID);
                  treeNode.setWeight(item.getSort());
                  treeNode.setExtra(
                      ImmutableMap.of(
                          "dataCondition", item.getId().toString(), "type", "tableType"));
                  return treeNode;
                })
            .collect(Collectors.toList());
    treeNodeList.addAll(finalTreeNodeList);
    List<Tree<String>> treeList =
        TreeUtil.build(treeNodeList, StrUtil.toString(Constants.DEFAULT_TOP_TREE_PID));
    return CollectionUtils.isEmpty(treeList) ? new ArrayList() : treeList;
  }

  @Override
  public List<ArrangeArchTypeListVO> archTypeList(ArrangeArchTypeQueryDTO req) {
    ArchiveTypeQueryDTO archiveTypeQueryDTO = new ArchiveTypeQueryDTO();
    archiveTypeQueryDTO.setFondsId(SecurityUtil.getFondsId());
    archiveTypeQueryDTO.setComId(SecurityUtil.getCropId());
    List<ArchiveTypeListVO> archList = archiveTypeSvc.selectList(archiveTypeQueryDTO);
    if (ObjectUtil.isEmpty(archList)) {
      return Lists.newArrayList();
    }
    Long status = SecurityUtil.getManageStatus();
    return archList.parallelStream()
        .sorted(Comparator.comparing(ArchiveTypeListVO::getSort))
        .map(
            item -> {
              ArrangeArchTypeListVO vo = ConvertUtil.toBean(item, ArrangeArchTypeListVO.class);
              Document qryDoc = new Document();
              qryDoc.setArchTypeId(item.getId());
              qryDoc.setTableId(item.getDocTableId());
              if (Objects.nonNull(req.getStatus())) {
                qryDoc.setStatus(req.getStatus());
              }
              qryDoc.setManageStatus(status);
              long docCount = documentSvc.count(qryDoc);
              vo.setDocNum(docCount);

              if (item.getHaveVol()) {
                Volume qryVol = new Volume();
                qryVol.setArchTypeId(item.getId());
                qryVol.setTableId(item.getVolTableId());
                if (Objects.nonNull(req.getStatus())) {
                  qryVol.setStatus(req.getStatus());
                }
                qryVol.setManageStatus(status);
                long volCount = volumeSvc.count(qryVol);
                vo.setVolNum(volCount);
              }
              if (item.getHaveBox()) {
                Box qryBox = new Box();
                qryBox.setArchTypeId(item.getId());
                qryBox.setTableId(item.getBoxTableId());
                qryBox.setManageStatus(status);
                if (Objects.nonNull(req.getStatus())) {
                  qryBox.setStatus(req.getStatus());
                }
                long boxCount = boxSvc.count(qryBox);
                vo.setBoxNum(boxCount);
              }

              if (item.getHaveProject()) {
                Project qryProj = new Project();
                qryProj.setArchTypeId(item.getId());
                qryProj.setTableId(item.getProjTableId());
                qryProj.setStatus(ArchStatusEnum.S0.getCode());
                long projCount = projectSvc.count(qryProj);
                vo.setProjNum(projCount);
                // 已完成
                qryProj.setStatus(ArchStatusEnum.S1.getCode());
                long finishProj = projectSvc.count(qryProj);
                vo.setFinishProjNum(finishProj);
              }
              return vo;
            })
        .collect(Collectors.toList());
  }

  @Override
  public Map<String, Object> getExtendData(SystemBO req) {
    return extendDataCmd.buildExtendData(req);
  }

  @Override
  public List<FormSchemaVO> getGroupFormSchema(BaseQueryDTO req) {
    // 获取分组规则
    RuleFieldQueryDTO fieldQueryDTO = BeanUtil.copyProperties(req, RuleFieldQueryDTO.class);
    if (Objects.isNull(req.getYear())) {
      fieldQueryDTO.setYear(DateUtil.year(new Date()));
    }

    setRuleByProjId(req.getArchTypeId(), req.getProjId(), fieldQueryDTO);
    List<ArchiveRuleFieldListVO> lists = archiveRuleMainSvc.getRuleFieldByVersion(fieldQueryDTO);
    if (ObjectUtil.isEmpty(lists)) {
      throw BizTipException.instance(ErrorCode.ARCHIVE_VERSION_NOT_FOUND, "未找到版本信息");
    }
    List<String> dbFieldKeyList =
        lists.stream()
            .filter(
                item ->
                    !StrUtil.equals(ArchConst.DOC_SEQUENCE_DB, item.getFieldCode())
                        && !StrUtil.equals(ArchConst.VOL_SEQUENCE_DB, item.getFieldCode()))
            .map(ArchiveRuleFieldListVO::getFieldCode)
            .collect(Collectors.toList());
    req.setDbFieldKeyList(dbFieldKeyList);

    ArchiveTypeVO archiveType = archiveTypeSvc.view(req.getArchTypeId());
    ArchiveFieldQueryDTO queryDTO = BeanUtil.copyProperties(req, ArchiveFieldQueryDTO.class);
    //    queryDTO.setAttrUse(BooleanUtil.toBoolean(YesNoEnum.YES.getCode()));
    List<ArchiveFieldListVO> list = archiveFieldSvc.selectListByform(queryDTO);

    Map<Long, List<ArchiveFieldListVO>> collectForm =
        list.stream()
            .filter(x -> dbFieldKeyList.contains(x.getDbFieldKey()))
            .peek(
                item -> {
                  if (ObjectUtil.isNull(item.getFieldGroup())) {
                    item.setFieldGroup(FieldGroupEnum.OTHER.getCode());
                  }
                })
            .sorted(
                Comparator.comparing(ArchiveFieldListVO::getFieldGroup)
                    .thenComparing(ArchiveFieldListVO::getSort))
            .collect(Collectors.groupingBy(ArchiveFieldListVO::getFieldGroup));
    List<FormSchemaVO> schema = Lists.newArrayList();

    // 循环组装数据
    collectForm.forEach(
        (key, fieldItems) -> {
          FieldGroupEnum anEnum = FieldGroupEnum.getEnum(key);
          schema.add(
              FormSchemaVO.builder()
                  .field("Divider" + key)
                  .component("Divider")
                  .label(anEnum.getDesc())
                  .componentProps(JSONUtil.createObj().putOpt("style", dividerStyle()))
                  .colProps(JSONUtil.createObj().putOpt("span", 24))
                  .show(true)
                  .build());
          schema.addAll(conversionList(fieldItems, archiveType, req.getYear(), req.getStatus()));
        });

    if (ObjectUtil.isEmpty(schema)) {
      return Lists.newArrayList();
    }
    schema.forEach(
        item -> {
          item.setColProps(JSONUtil.createObj().putOpt("span", 24));
          JSONObject componentProps = item.getComponentProps();
          if (item.getField().equals(ArchConst.CLASSFY)
              || item.getField().equals(StringUtils.camelToUnderline(ArchConst.CLASSFY_NAME))) {
            componentProps.putOpt(
                "fieldNames",
                JSONUtil.createObj()
                    // 分类号 分类名称 label统一显示拼接的name
                    .putOpt("label", "name")
                    .putOpt("key", "id")
                    .putOpt("value", "name"));
          }
          componentProps.putOpt("disabled", false);
        });
    return schema;
  }

  @Override
  public void refreshCatalog(Catalog req) {
    ArchiveTypeVO view = archiveTypeSvc.view(req.getArchTypeId());
    if (ObjectUtil.isEmpty(view)) {
      return;
    }
    refreshCatalogWithTableId(view.getId(), view.getDocTableId(), ArchStatusEnum.S0.getCode());
    refreshCatalogWithTableId(view.getId(), view.getDocTableId(), ArchStatusEnum.S1.getCode());
    if (BooleanUtil.isTrue(view.getHaveVol())) {
      refreshCatalogWithTableId(view.getId(), view.getVolTableId(), ArchStatusEnum.S0.getCode());
      refreshCatalogWithTableId(view.getId(), view.getVolTableId(), ArchStatusEnum.S1.getCode());
    }
    if (BooleanUtil.isTrue(view.getHaveBox())) {
      refreshCatalogWithTableId(view.getId(), view.getBoxTableId(), ArchStatusEnum.S0.getCode());
      refreshCatalogWithTableId(view.getId(), view.getBoxTableId(), ArchStatusEnum.S1.getCode());
    }

    if (BooleanUtil.isTrue(view.getHaveProject())) {
      refreshCatalogWithTableId(view.getId(), view.getProjTableId(), ArchStatusEnum.S0.getCode());
      refreshCatalogWithTableId(view.getId(), view.getProjTableId(), ArchStatusEnum.S1.getCode());
    }
  }

  public void refreshCatalogWithTableId(Long archTypeId, String tableId, Integer status) {
    CompletableFuture.runAsync(
        () -> {
          // 刷新待整理目录树
          Catalog collection = new Catalog();
          collection.setTableId(tableId);
          collection.setArchTypeId(archTypeId);
          collection.setManageType(status);
          refreshCatalogCmd.refresh(collection);
        });
  }

  @Override
  public Integer getMaxVolumeSequence(Map<String, Object> condition, String tableId) {
    String sequence = volumeSvc.getSequence(condition, tableId, false);
    return Integer.parseInt(sequence);
  }

  @Override
  public Integer getMaxDocumentSequence(Map<String, Object> condition, String tableId) {
    String sequence = documentSvc.getSequence(condition, tableId);
    return Integer.parseInt(sequence);
  }

  @Override
  public void createEsIndex(CreateEsIndexDTO req) {
  }
}
